# @Time : 2021/6/18 21:41
# @Author : Li Kunlun
# @Description : Variable变量
import torch
from torch.autograd import Variable  # torch 中 Variable 模块

print("----------1、Variable基本介绍----------------------------------")
# 先生鸡蛋
tensor = torch.FloatTensor([[1, 2], [3, 4]])
# 把鸡蛋(数据)放到篮子（variable变量）里, 数据会改变，requires_grad是参不参与误差反向传播, 要不要计算梯度
# 可以假设variable作为神经网络中的节点
variable = Variable(tensor, requires_grad=True)

print(tensor)
"""
tensor([[1., 2.],
        [3., 4.]])
"""

print(variable)
"""
tensor([[1., 2.],
        [3., 4.]], requires_grad=True)
"""

print("----------2、Variable 计算, 梯度----------------------------------")
t_out = torch.mean(tensor * tensor)  # x^2
v_out = torch.mean(variable * variable)  # x^2
# tensor(7.5000)
# tensor(7.5000, grad_fn=<MeanBackward0>)
"""
到目前为止, 我们看不出什么不同, 但是时刻记住, Variable 计算时, 它在背景幕布后面一步步默默地搭建着
一个庞大的系统, 叫做计算图(computational graph). 该图将所有的计算步骤 (节点) 都连接起来, 最后进行误差反向传递的时候, 
一次性将所有 variable 里面的修改幅度 (梯度) 都计算出来, 而 tensor 就没有这个能力。
"""
print(t_out)
print(v_out)

"""
1、Variable 是计算图的一部分, 可以用来传递误差就好.
2、计算图中的 v_out 计算步骤:v_out = 1/4 * sum(variable*variable) 
3、v_out 的梯度计算公式如下：, d(v_out)/d(variable) = 1/4*2*variable = variable/2
"""
v_out.backward()  # 模拟 v_out 的误差反向传递
# tensor([[0.5000, 1.0000],
#         [1.5000, 2.0000]])
print(variable.grad)  # 初始 Variable 的梯度

print("----------3、获取 Variable 里面的数据----------------------------------")
"""
直接print(variable)只会输出 Variable 形式的数据, 在很多时候是用不了的(比如想要用 plt 画图), 
所以需要转换一下, 将它变成 tensor 形式
"""
print(variable.data)  # tensor 形式
"""
tensor([[1., 2.],
        [3., 4.]])
"""
print(type(variable.data))  # 类型：<class 'torch.Tensor'>

# 转化为numpy 形式
print(variable.data.numpy())
"""
[[ 1.  2.]
 [ 3.  4.]]
"""
